package session.stateless.SCM;

import javaclass.ProductType;
import entity.SCM.Batch;
import entity.SCM.DeliveryOrder;
import entity.SCM.MaterialLine;
import entity.SCM.MaterialLineItem;
import entity.SCM.MaterialRequest;
import entity.SCM.PickingOrder;
import entity.SCM.ProductBalance;
import entity.SCM.ProductLine;
import entity.SCM.ProductLineItem;
import entity.RawMaterial;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

@Stateless
public class BatchBean implements BatchBeanLocal {
    
    @PersistenceContext()
    EntityManager em;
    
    DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");

    @Override
    public Long createProductBatch(ProductType productType, Date expiryDate, Long quantity, String location) {
        
        Batch batch = new Batch();
        
        ProductBalance product = em.find(ProductBalance.class, productType); 
        if(product == null){
            System.out.println("Product Not Exist!");
            return null;
        }
        Date batchDate = new Date();
        batch.setBatchType(Batch.BatchType.Product);
        batch.setStatus(Batch.Status.In);
        batch.setBatchDate(batchDate);
        batch.setExpiryDate(expiryDate);
        batch.setLocation(location);
        batch.setQuantity(quantity);
        batch.setProduct(product);
        em.persist(batch);
        em.flush();
        
        long a = product.getBalance().longValue();
        long b = quantity.longValue();
        long sum = a + b;
        product.setBalance(new Long(sum));
        em.flush();
        
        return batch.getId();
    }

    @Override
    public List<Batch> getProductBatch() {
        
        Query q = em.createQuery("SELECT c FROM Batch c WHERE c.batchType = :batchType AND c.status = :status");
        q.setParameter("batchType", Batch.BatchType.Product);
        q.setParameter("status", Batch.Status.In);
        
        return q.getResultList();
    }

    @Override
    public Long createMaterialBatch(String materialType, Date expiryDate, Long quantity, String location) {
        Batch batch = new Batch();
        
        RawMaterial rawMaterial = em.find(RawMaterial.class, materialType); 
        if(rawMaterial == null){
            System.out.println("Raw Material Not Exist!");
            return null;
        }
        
        Date batchDate = new Date();
        batch.setBatchType(Batch.BatchType.Material);
        batch.setStatus(Batch.Status.In);
        batch.setBatchDate(batchDate);
        batch.setExpiryDate(expiryDate);
        batch.setLocation(location);
        batch.setQuantity(quantity);
        batch.setMaterial(rawMaterial);
        em.persist(batch);
        em.flush();
        
        long a = rawMaterial.getInventoryBalance().longValue();
        long b = quantity.longValue();
        long sum = a + b;
        rawMaterial.setInventoryBalance(new Long(sum));
        em.flush();

        return batch.getId();
    }

    @Override
    public List<Batch> getMaterialBatch() {

        Query q = em.createQuery("SELECT c FROM Batch c WHERE c.batchType = :batchType AND c.status = :status");
        q.setParameter("batchType", Batch.BatchType.Material);
        q.setParameter("status", Batch.Status.In);

        return q.getResultList();
    }

    @Override
    public List<ProductBalance> getProduct() {

        Query q = em.createQuery("SELECT c FROM ProductBalance c");
        return q.getResultList();
    }

    @Override
    public List<RawMaterial> getMaterial() {

        Query q = em.createQuery("SELECT c FROM RawMaterial c");
        return q.getResultList();
    }
    
    @Override
    public List<String> getMaterialType() {

        List<String> materialTypes = new ArrayList<String>();
        Query q = em.createQuery("SELECT c FROM RawMaterial c");
        List<RawMaterial> rawMaterials =  q.getResultList();
        for(int i=0; i<rawMaterials.size();i++){
            materialTypes.add(rawMaterials.get(i).getMaterialType());
        }
        
        return materialTypes;
    }

    @Override
    public List<MaterialLineItem> allocateMaterialBatch(Long materialLineId) {

        MaterialLine materialLine = em.find(MaterialLine.class, materialLineId);
        if (materialLine == null) {
            System.out.println("This MaterialLine Does Not Exist!");
            return null;
        }

        Query q = em.createQuery("SELECT c FROM Batch c WHERE c.status = :status AND c.batchType = :batchType ORDER BY c.expiryDate ");
        q.setParameter("batchType", Batch.BatchType.Material);
        q.setParameter("status", Batch.Status.In);
        List<Batch> materialBatch = q.getResultList();

        String materialType = materialLine.getRawMaterial().getMaterialType();

        for (int i = materialBatch.size() - 1; i >= 0; i--) {
            if (!materialBatch.get(i).getMaterial().getMaterialType().equals(materialType)) {
                materialBatch.remove(i);
            }
        }

        if (materialBatch.isEmpty()) {
            return null;
        }

        long amount = materialLine.getQuantity().longValue();
        long temp;
        List<MaterialLineItem> materialLineItems = new ArrayList<MaterialLineItem>();

        while (amount > 0) {
            MaterialLineItem materialLineItem = new MaterialLineItem();
            Batch batch = materialBatch.get(0);
            temp = batch.getQuantity().longValue();
            if (amount <= temp) {
                materialLineItem.setQuantity(new Long(amount));//Create New MaterialLineItem
                materialLineItem.setBatch(batch);
                em.persist(materialLineItem);
                batch.setQuantity(new Long(temp - amount)); // Update the Batch quantity
                long a = batch.getMaterial().getInventoryBalance().longValue();
                a -= amount;
                batch.getMaterial().setInventoryBalance(new Long(a));//Update the Raw material balance
                em.flush();
            } else {
                materialLineItem.setQuantity(new Long(temp));
                materialLineItem.setBatch(batch);
                em.persist(materialLineItem);
                batch.setQuantity(new Long(0));
                batch.setStatus(Batch.Status.Out);
                em.flush();
                materialBatch.remove(0);
                long a = batch.getMaterial().getInventoryBalance().longValue();
                a -= temp;
                batch.getMaterial().setInventoryBalance(new Long(a));
                em.flush();
            }

            amount -= temp;
            materialLineItems.add(materialLineItem);
        }

        return materialLineItems;
    }
    
    @Override
    public boolean processMaterialRequest(Long requestId){
        
        MaterialRequest materialRequest = em.find(MaterialRequest.class, requestId);
        if(materialRequest==null){
            System.out.println("This Material Request Does Not Exist!");
            return false;
        }
        
        List<MaterialLine> materialLines = materialRequest.getMaterialLines();
        if(materialLines.isEmpty()){
            return false;
        }
        
        List<MaterialLineItem> materialLineItems = new ArrayList<MaterialLineItem>();
        for (int i = 0; i < materialLines.size(); i++) {
            List<MaterialLineItem> tempList = this.allocateMaterialBatch(materialRequest.getMaterialLines().get(i).getMaterialLineId());
            
            for(int j=0; j<tempList.size(); j++){
                MaterialLineItem materialLineItem = tempList.get(j);
                materialLineItems.add(materialLineItem);
            }
        }
        
        materialRequest.setMaterialLineItems(materialLineItems);
        em.flush();
        return true;
    }
    
    @Override
    public List<ProductLineItem> allocateProductBatch(Long productLineId) {

        ProductLine productLine = em.find(ProductLine.class, productLineId);
        if (productLine == null) {
            System.out.println("This Product Line Does Not Exist!");
            return null;
        }

        Query q = em.createQuery("SELECT c FROM Batch c WHERE c.status = :status AND c.batchType = :batchType ORDER BY c.expiryDate ");
        q.setParameter("batchType", Batch.BatchType.Product);
        q.setParameter("status", Batch.Status.In);
        List<Batch> productBatch = q.getResultList();

        ProductType productType = productLine.getProduct().getProductType();

        for (int i = productBatch.size() - 1; i >= 0; i--) {
            if (!productBatch.get(i).getProduct().getProductType().equals(productType)) {
                productBatch.remove(i);
            }
        }

        if (productBatch.isEmpty()) {
            return null;
        }

        long amount = productLine.getQuantity().longValue();
        long temp;
        List<ProductLineItem> productLineItems = new ArrayList<ProductLineItem>();

        while (amount > 0) {
            ProductLineItem productLineItem = new ProductLineItem();
            Batch batch = productBatch.get(0);
            temp = batch.getQuantity().longValue();
            if (amount <= temp) {
                productLineItem.setQuantity(new Long(amount));//Create New ProductLineItem
                productLineItem.setBatch(batch);
                em.persist(productLineItem);
                em.flush();
                batch.setQuantity(new Long(temp - amount)); // Update the Batch quantity
                long a = batch.getProduct().getBalance().longValue();
                a -= amount;
                batch.getProduct().setBalance(new Long(a));//Update the ProductBalance balance
                em.flush();
            } else {
                productLineItem.setQuantity(new Long(temp));
                productLineItem.setBatch(batch);
                em.persist(productLineItem);
                em.flush();
                batch.setQuantity(new Long(0));
                batch.setStatus(Batch.Status.Out);
                em.flush();
                productBatch.remove(0);
                long a = batch.getProduct().getBalance().longValue();
                a -= temp;
                batch.getProduct().setBalance(new Long(a));
                em.flush();
            }
            
            if(batch.getQuantity().longValue()==0){
                batch.setStatus(Batch.Status.Out);
            }

            amount -= temp;
            productLineItems.add(productLineItem);
        }

        return productLineItems;
    }
    
    @Override
    public boolean completePickingOrder(Long deliveryOrderId){
        
        DeliveryOrder deliveryOrder = em.find(DeliveryOrder.class, deliveryOrderId);
        if (deliveryOrder == null) {
            System.out.println("This Delivery Order Does Not Exist!");
            return false;
        }
        
        List<ProductLine> productLines = deliveryOrder.getProductLines();
        
        if(productLines.isEmpty()){
            return false;
        }
        
        List<ProductLineItem> productLineItems = new ArrayList<ProductLineItem>();
        
        for(int i =0; i<productLines.size();i++){
            
            List<ProductLineItem> tempList = this.allocateProductBatch(productLines.get(i).getId());
            
            for(int j=0; j<tempList.size();j++){
                ProductLineItem productLineItem = tempList.get(j);
                productLineItems.add(productLineItem);
            }
        }
        
        deliveryOrder.getPickingOrder().setProductLineItems(productLineItems);
        em.flush();
        
        return true;
    }
    
    @Override
    public List<MaterialLineItem> getMaterialLineItem( Long materialRequestId ){
        
        MaterialRequest materialRequest = em.find(MaterialRequest.class, materialRequestId);
        if(materialRequest ==null){
            System.out.println("This Material Request Does Not Exist!");
            return null;
        }
   
        return materialRequest.getMaterialLineItems();
    }
    
    @Override
    public List<ProductLineItem> getProductLineItem( Long pickingOrderId ){
        
        PickingOrder pickingOrder = em.find(PickingOrder.class, pickingOrderId);
        if(pickingOrder ==null){
            System.out.println("This Picking Order Does Not Exist!");
            return null;
        }
        
        return pickingOrder.getProductLineItems();
    }
    
    @Override
    public ProductBalance getProductBalance(ProductType productType){
        ProductBalance productBalance = em.find(ProductBalance.class, productType);
        
        if(productBalance == null){
            System.out.print("This ProductBalance Not EXIST!");
            return null;
        }
        else
            return productBalance;
    }
    
    @Override
    public RawMaterial findRawMaterial(String materialType){
        
        RawMaterial rawMaterial = em.find(RawMaterial.class, materialType);
        
        if(rawMaterial == null){
            System.out.println("This Raw Material Not Exist!");
            return null;
        }
        else
            return rawMaterial;
    }
    
}
